package com.ouroboroswiki.core.content.xml;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.ouroboroswiki.core.AbstractContent;
import com.ouroboroswiki.core.Content;
import com.ouroboroswiki.core.ContentException;
import com.ouroboroswiki.core.ContentValidationResult;
import com.ouroboroswiki.core.User;

public class XMLMetadataContent extends AbstractContent {
	
	private static final String ABOUT = "about";
	
	private static final String ELEMENT_METADATA = "meta";
	private static final String ELEMENT_VALIDATION = "validation";
	
	private static final String ATTRIBUTE_PATH = "path";
	private static final String ATTRIBUTE_PIPE = "pipe";
	private static final String ATTRIBUTE_REPO = "repo";
	private static final String ATTRIBUTE_WRITABLE = "writable";
	
	private static final String ATTRIBUTE_MESSAGE = "message";
	private static final String ATTRIBUTE_LEVEL = "level";
	
	private static final String ATTRIBUTE_KEY = "key";
	
	private DocumentBuilderFactory documentBuilderFactory;
	private TransformerFactory transformerFactory;
	private Content content;
	
	public XMLMetadataContent( Content content, DocumentBuilderFactory documentBuilderFactory, TransformerFactory transformerFactory ) {
		this.content = content;
		this.documentBuilderFactory = documentBuilderFactory;
		this.transformerFactory = transformerFactory;
	}

	@Override
	public Map<String, Content> getChildContent() {
		HashMap<String, Content> childContent = new HashMap<String, Content>(1);
		childContent.put( ABOUT, content );
		return childContent;
	}

	@Override
	public void write(OutputStream outs) throws IOException, ContentException {
		try {
			Document document = documentBuilderFactory.newDocumentBuilder().newDocument();
			Element contentNode = create( document, content );
			document.appendChild(contentNode);
			try {
				Transformer transformer = transformerFactory.newTransformer();
				transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes");
				transformer.setOutputProperty( OutputKeys.INDENT, "yes");
				StreamResult result = new StreamResult(outs);
				DOMSource source = new DOMSource(document);
				transformer.transform(source, result);
			} catch( TransformerException ex ) {
				throw new ContentException( "unable to create transformer", ex );
			}
		} catch( ParserConfigurationException ex ) {
			throw new ContentException( "unable to create document builder", ex );
		}
	}
	
	public Element create( Document document, Content content ) {
		String uniqueName = content.getUniqueName();
		String repositoryName = content.getRepositoryName();
		User updater = content.getUpdateEditor();
		Date updateTime = content.getUpdateTime();
		String mimeType = content.getMimeType();
		Object version = content.getVersion();
		Collection<ContentValidationResult> validationResults = content.getValidationResults();
		boolean writable = content.isWritable();
		boolean readable = content.isReadable();
		boolean dynamic = content.isDynamic();
		
		Element element = document.createElement(ELEMENT_METADATA);
		if( uniqueName != null ) {
			element.setAttribute(ATTRIBUTE_PATH, uniqueName);
		}
		if( repositoryName != null ) {
			element.setAttribute(ATTRIBUTE_REPO, repositoryName);
		}
		element.setAttribute( ATTRIBUTE_WRITABLE, Boolean.toString( writable ) );
		if( validationResults != null ) {
			for( ContentValidationResult validationResult : validationResults ) {
				Element validationElement = document.createElement(ELEMENT_VALIDATION);
				validationElement.setAttribute(ATTRIBUTE_MESSAGE, validationResult.getMessage());
				validationElement.setAttribute(ATTRIBUTE_LEVEL, validationResult.getLevel().name());
				element.appendChild(validationElement);
			}
		}
		Map<String, Content> childContents = content.getChildContent();
		if( childContents != null ) {
			for( Map.Entry<String, Content> childContentEntry : childContents.entrySet() ) {
				Element childContentElement = create( document, childContentEntry.getValue() );
				childContentElement.setAttribute( ATTRIBUTE_KEY, childContentEntry.getKey() );
				element.appendChild(childContentElement);
			}
		}
		return element;
		
	}

}
